# ========================================
# RISC-V 最小操作系统 Makefile
# ========================================
# 
# 这个Makefile用于编译RISC-V最小操作系统。
# Makefile是构建系统的配置文件，定义了如何编译、链接和生成最终的可执行文件。
# 
# 主要功能：
# - 编译C和汇编源文件
# - 链接生成内核镜像
# - 在QEMU中运行和调试
# - 清理构建文件
#

# ========================================
# 工具链配置
# ========================================
# 
# 定义编译工具，这些工具需要安装RISC-V工具链
# riscv64-unknown-elf-* 是标准的RISC-V交叉编译工具链

CC = riscv64-unknown-elf-gcc    # C编译器
LD = riscv64-unknown-elf-ld     # 链接器
OBJCOPY = riscv64-unknown-elf-objcopy  # 对象文件复制工具
OBJDUMP = riscv64-unknown-elf-objdump  # 反汇编工具

# ========================================
# 编译选项
# ========================================
# 
# CFLAGS：C编译器选项
# -Wall -Wextra：启用所有警告，帮助发现潜在问题
# -O2：优化级别2，平衡性能和编译时间
# -fno-omit-frame-pointer：保留栈帧指针，便于调试
# -ggdb：生成GDB调试信息
# -mcmodel=medany：使用中等代码模型，适合内核开发
# -ffreestanding：独立环境编译，不依赖标准库
# -fno-common：不将未初始化变量放在common段
# -nostdlib：不使用标准库
# -mno-relax：禁用链接时优化
# -I./include：添加头文件搜索路径

CFLAGS = -Wall -Wextra -O2 -fno-omit-frame-pointer -ggdb
CFLAGS += -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax
CFLAGS += -I./include

# ========================================
# 链接选项
# ========================================
# 
# LDFLAGS：链接器选项
# -z max-page-size=4096：设置最大页面大小为4KB

LDFLAGS = -z max-page-size=4096

# ========================================
# 源文件定义
# ========================================
# 
# 定义需要编译的源文件
# SRCS：C源文件
# ASMS：汇编源文件

SRCS = kernel/boot/main.c kernel/boot/uart.c kernel/boot/printf.c kernel/boot/console.c kernel/mm/kalloc.c kernel/mm/vm.c
ASMS = kernel/boot/entry.S

# ========================================
# 目标文件定义
# ========================================
# 
# OBJS：编译生成的目标文件
# 通过字符串替换将.c和.S文件转换为.o文件

OBJS = $(SRCS:.c=.o) $(ASMS:.S=.o)

# ========================================
# 目标文件定义
# ========================================
# 
# KERNEL：ELF格式的内核文件（包含调试信息）
# KERNEL_BIN：二进制格式的内核文件（QEMU可以直接加载）

KERNEL = kernel.elf
KERNEL_BIN = kernel.bin

# ========================================
# 默认目标
# ========================================
# 
# 当运行make命令时，默认执行这个目标
# 生成二进制内核文件

all: $(KERNEL_BIN)

# ========================================
# 链接内核
# ========================================
# 
# 将所有的目标文件链接成ELF格式的内核
# -T kernel/kernel.ld：指定链接脚本
# -o $@：输出文件名（KERNEL变量）
# $(OBJS)：所有目标文件

$(KERNEL): $(OBJS) kernel/kernel.ld
	$(LD) $(LDFLAGS) -T kernel/kernel.ld -o $@ $(OBJS)

# ========================================
# 生成二进制镜像
# ========================================
# 
# 将ELF文件转换为二进制文件，QEMU可以直接加载
# -O binary：输出格式为二进制

$(KERNEL_BIN): $(KERNEL)
	$(OBJCOPY) -O binary $< $@

# ========================================
# 编译C文件
# ========================================
# 
# 将.c文件编译为.o目标文件
# $<：第一个依赖文件（.c文件）
# $@：目标文件（.o文件）

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

# ========================================
# 编译汇编文件
# ========================================
# 
# 将.S汇编文件编译为.o目标文件

%.o: %.S
	$(CC) $(CFLAGS) -c $< -o $@

# ========================================
# 清理构建文件
# ========================================
# 
# 删除所有编译生成的文件
# 在重新编译前运行make clean

clean:
	rm -f $(OBJS) $(KERNEL) $(KERNEL_BIN)

# ========================================
# 在QEMU中运行
# ========================================
# 
# 使用QEMU运行编译好的内核
# -machine virt：使用virt机器类型
# -bios none：不使用BIOS
# -kernel $(KERNEL_BIN)：加载内核文件
# -nographic：不使用图形界面

run: $(KERNEL_BIN)
	qemu-system-riscv64 -machine virt -bios none -kernel $(KERNEL_BIN) -nographic

# ========================================
# 调试模式
# ========================================
# 
# 启动QEMU调试模式，等待GDB连接
# -s：启动GDB服务器，监听端口1234
# -S：启动时暂停，等待GDB连接

debug: $(KERNEL_BIN)
	qemu-system-riscv64 -machine virt -bios none -kernel $(KERNEL_BIN) -nographic -s -S

# ========================================
# 反汇编内核
# ========================================
# 
# 生成内核的反汇编代码，用于分析

disasm: $(KERNEL)
	$(OBJDUMP) -d $<

# ========================================
# 显示内核符号表
# ========================================
# 
# 显示内核中的所有符号（函数、变量等）

symbols: $(KERNEL)
	$(OBJDUMP) -t $<

# ========================================
# 伪目标声明
# ========================================
# 
# 声明这些目标不是文件，而是命令
# 这样即使存在同名文件，make也会执行这些目标

.PHONY: all clean run debug disasm symbols
